#include "StdAfx.h"
#include "Convertor.h"
#include "greta\regexpr2.h"

using namespace regex;


/**
 * Downloader working thread procedure
 * @param lpParam Pointer to DownloadEngine object
 */
DWORD WINAPI ThreadProc(LPVOID lpParam) {
	CConvertor *c = (CConvertor *)lpParam;
	return c->ThreadFunc();
} // DownloaderThreadProc

char *W2UTF(wchar_t *wstring) {
	size_t bufflen = WideCharToMultiByte(CP_UTF8, 0, wstring, -1, NULL, 0, NULL, NULL)*sizeof(char);
	char *buffer = (char *)malloc(bufflen * sizeof(char));
	WideCharToMultiByte(CP_UTF8, 0, wstring, -1, buffer, bufflen, NULL, NULL);
	return buffer;
} // W2UTF()



CConvertor::CConvertor(void) :
	m_running(FALSE),
	m_stopThread(FALSE)
{
}

CConvertor::~CConvertor(void)
{
}

/**
 * Lock access to data members
 */
void CConvertor::Lock() 
{
	EnterCriticalSection(&m_csData);
} // Lock()

/**
 * Unlock access to data members
 */
void CConvertor::Unlock()
{
	LeaveCriticalSection(&m_csData);
} // Unlock()

void CConvertor::Start()
{
	DWORD threadId;
	m_hThread = CreateThread(NULL, 0, ThreadProc, this, 0, &threadId);
	m_running = TRUE;
	m_stopThread = FALSE;
}

void CConvertor::Stop()
{
	m_stopThread = TRUE;
}

void CConvertor::Close()
{
	// Wait while working thread closes
	if (m_hThread) {
		m_stopThread = TRUE;
		WaitForSingleObject(m_hThread, INFINITE);
	}
}

DWORD CConvertor::ThreadFunc() 
{
	m_count = 0;
	ProcessFolder(m_sourceFolder, COUNT);
	if (m_stopThread) {
		m_running = m_stopThread = FALSE;
		return 0;
	}

	m_count = 0;
	ProcessFolder(m_sourceFolder, CONVERT);

	m_running = m_stopThread = FALSE;
	theApp.PostThreadMessage(WM_THREAD, (WPARAM)TM_FINISHED, (LPARAM)m_count);
	return 0;
}

void CConvertor::ProcessFolder(CString aFolder, PassType aPass)
{
//  Spocitam dlazdice v adresari
	WIN32_FIND_DATA FileData;   // Data structure describes the file found
	HANDLE hSearch;             // Search handle returned by FindFirstFile
	CString DirMask = aFolder + _T("\\*.*");

	hSearch = FindFirstFile (DirMask, &FileData);
	
	if (aPass == CONVERT) m_mapFile->BeginTransaction();

	if (hSearch != INVALID_HANDLE_VALUE) {
		do {
			if (FileData.dwFileAttributes == FILE_ATTRIBUTE_DIRECTORY) {
				if (wcscmp(FileData.cFileName, _T(".")) != 0 && wcscmp(FileData.cFileName, _T("..")) != 0)
					ProcessFolder(aFolder + _T("\\") + FileData.cFileName, aPass);
			} else {
				m_count++;
				if (aPass == CONVERT) {
					switch (m_sourceType) {
/* **************************************************************
     G E O K U K
   **************************************************************/
						case GEOKUK: {
							CString f = aFolder + _T("\\") + FileData.cFileName;
							f.Replace(m_sourceFolder, _T(""));

							std::wstring file = f.GetBuffer();

							match_results results;
							rpattern pat(_T("^\\\\[0-9a-f]{2}\\\\[0-9a-f]{2}\\\\[0-9a-f]{2}\\\\[0-9a-f]{2}\\\\.*\\.kachle"));
							match_results::backref_type br = pat.match(file, results);
							if(br.matched) {
								CString fileName = FileData.cFileName;
								int pos = 0;
								CString token;
								int tokenNo = 0;
								CString name;
								int x, y, z;

								fileName.Replace(_T(".kachle"), _T(""));
								fileName.Replace(_T("base_n"), _T("base-n"));
								token = fileName.Tokenize(_T("_"), pos);
								while (token != _T("")) {
									switch (tokenNo) {
										case 0:
											name = token;
											name.Replace(_T("base-n"), _T("base_n"));
											break;
										case 1:
											swscanf_s(token.GetBuffer(), _T("%d"), &z);
											break;
										case 2: 
											x = hex2dec(token);
											break;
										case 3: 
											y = hex2dec(token);
											break;
									}
									token = fileName.Tokenize(_T("_"), pos);
									tokenNo++;
								} // while
								CFile tile;
								if (tile.Open(aFolder + _T("\\") + FileData.cFileName, CFile::modeRead)) {
									BYTE* tileBuf = (BYTE*)GlobalAlloc(GMEM_FIXED, FileData.nFileSizeLow);
									tile.Read(tileBuf, FileData.nFileSizeLow);

									try {
										char* utf8_type = W2UTF(name.GetBuffer());

										SQLStatement qry = m_mapFile->Prepare(_T("INSERT INTO `tiles` (`x`, `y`, `z`, `s`, `image`) VALUES(?, ?, ?, ?, ?)"));
										qry.Bind(1, x);
										qry.Bind(2, y);
										qry.Bind(3, 17-z);
										qry.Bind(4, utf8_type);
										qry.Bind(5, tileBuf, FileData.nFileSizeLow);
										qry.Step();

										free(utf8_type);
									} catch (SQLite::Error e) {
										m_mapFile->Rollback();
										::AfxMessageBox(e.getErrorMessage(), MB_OK | MB_ICONEXCLAMATION);
										Stop();
										FindClose(hSearch);
										return;
									} // try

									GlobalFree(tileBuf);
								}
							} // if br.matches
							break;
							}
/* ********************************************************
       O S M
   ********************************************************/	    
						case OSMTILE: {
							CString fileName = aFolder + _T("\\") + FileData.cFileName;
							fileName.Replace(m_sourceFolder, _T(""));
							std::wstring file = fileName.GetBuffer();

							match_results results;
							rpattern pat(_T("^\\\\[0-9]{2}\\\\[0-9]{2,}\\\\[0-9]{2,}\\.(png|jpg)"));
							match_results::backref_type br = pat.match(file, results);
							if(br.matched) {
								int pos = 0;
								CString token;
								int tokenNo = 0;
								int x, y, z;

								fileName.Replace(_T("."), _T("\\"));
								token = fileName.Tokenize(_T("\\"), pos);
								while (token != _T("")) {
									switch (tokenNo) {
										case 0:
											swscanf_s(token.GetBuffer(), _T("%d"), &z);
											break;
										case 1: 
											swscanf_s(token.GetBuffer(), _T("%d"), &x);
											break;
										case 2: 
											swscanf_s(token.GetBuffer(), _T("%d"), &y);
											break;
									}
									token = fileName.Tokenize(_T("\\"), pos);
									tokenNo++;
								} // while
								CFile tile;
								if (tile.Open(aFolder + _T("\\") + FileData.cFileName, CFile::modeRead)) {
									BYTE* tileBuf = (BYTE*)GlobalAlloc(GMEM_FIXED, FileData.nFileSizeLow);
									tile.Read(tileBuf, FileData.nFileSizeLow);

									try {
										SQLStatement qry = m_mapFile->Prepare(_T("INSERT INTO `tiles` (`x`, `y`, `z`, `s`, `image`) VALUES(?, ?, ?, ?, ?)"));
										qry.Bind(1, x);
										qry.Bind(2, y);
										qry.Bind(3, 17-z);
										qry.Bind(4, 0);
										qry.Bind(5, tileBuf, FileData.nFileSizeLow);
										qry.Step();
									} catch (SQLite::Error e) {
										m_mapFile->Rollback();
										::AfxMessageBox(e.getErrorMessage(), MB_OK | MB_ICONEXCLAMATION);
										Stop();
										FindClose(hSearch);
										return;
									} // try

									GlobalFree(tileBuf);
								}
							}
							break;
							}
						} // switch
					} // if
				
			} // if dir
			if (m_stopThread) return;
		} // do 
		while (FindNextFile (hSearch, &FileData));
	} // if hSearch 
	FindClose(hSearch);

	switch (aPass) {
		case COUNT:
			theApp.PostThreadMessage(WM_THREAD, (WPARAM)TM_COUNT, (LPARAM)m_count);
			break;
		case CONVERT:
			m_mapFile->Commit();
			theApp.PostThreadMessage(WM_THREAD, (WPARAM)TM_CONVERT, (LPARAM)m_count);
			break;
	}
}

int CConvertor::hex2dec(CString hex)
{
	int num = 0;
	hex = hex.Trim();
	hex = hex.MakeUpper();
	for(int i=0; i<hex.GetLength(); i++) {
		TCHAR c = hex.GetAt(i);
		if (!((c >= '0' && c <= '9')||(c >= 'A' && c <= 'F'))) {			
			return 0;
		}
		int n;
		if (c >= '0' && c <= '9') n = c - '0';
		if (c >= 'A' && c <= 'F') n = c - 'A' + 10;
		num = num*16+n;
	}
	return num;
}